package com.hhoss.snid;

import com.hhoss.date.Formatter;
import com.hhoss.logs.Logger;

import java.util.Arrays;
import java.util.Date;

/**
 * return global unique 26W sequence in every second
 * will wait to next second if need more
 * @author kejun
 *
 */
public class Key {	
	private static final Logger logger = Logger.get();
	private static final int bitSize=18;
	private static final int bitMask=(1<<bitSize)-1;
	private static final int appMask=(1<<13)-1;
	private static final int appPart = SNID.appCode3()<<bitSize;
    private static long tims = SeqTims.val();
	private static int sn = 0;
    

    /**
     * <br/>返回19位长度的序列号，在每个应用实例的保证唯一性，
     * <br/>日期(14bit)+时间(17bit)+应用实例4位(13bit)+序列号6位(19bit)
     * <br/>单应用（运行实例数最大8000）支持每秒50万的序列号全局30年不重复；
     * <br/>序列生成时间大约0.1us；
     * <br/>bits usage rate about 48%: 12/16*31/32*24/32*60/64*60/64
     * @return next sequence for global 
     */
    public synchronized static long next(){
      	long now = SeqTims.val();
      	sn = sn+1 & bitMask;
    	if(now<tims){
    		logger.error("system clock error, current[{}] is before old[{}]",now,tims);
    		throw new IllegalArgumentException("system clock error!");
    	}
    	if(now>tims){
    		//logger.debug("tims:{}->{},seq:{}->0.",tims,now,seq);
     		sn=0;
    	}else if(sn==0){//wait to next time
     		logger.debug("cyclic too fast, wait to next second[{}].",tims);
    		while(SeqTims.val()<=tims)try{Thread.sleep(1);}catch(Exception e){}
      	}
    	tims=now;
    	return tims<<32 | appPart | sn ;
    }
    
    /**
     * @see #next()
     * @return Long.toString
     */
    public static String text(){
    	return Long.toString(next());
    }

    /**
     * @param lsn long generated by {@link #next()} or {@link #text()};
     * @return int[9]{y,yyyy,M,d,h,m,s,loc,seq} 
     */
    public static int[] meta(long lsn){    	
    	int[] flds = Arrays.copyOf(SeqTims.meta(lsn>>32), 9);
    		  flds[7]= appMask&(int)(lsn>>bitSize); // worker;
    		  flds[8]= bitMask&(int)(lsn); // cyclic Seq;
    	return flds;
    }  
     /**
     * @param lsn  value of seconds from 1970-1-1
     * @return yyyyMM
     */
    public static String yyyyMM(long lsn){
       	return Formatter.format(new Date((lsn>>32)*1000),"yyyyMM");
    }
   /**
     * @param lsn  value of seconds from 1970-1-1
     * @return yyyyMMdd
     */
    public static String yyyyMMdd(long lsn){
    	return Formatter.format(new Date((lsn>>32)*1000),"yyyyMMdd");
    }
    /**
     * @param lsn value of seconds from 1970-1-1
     * @return yyMMdd
     */
    public static String yyMMdd(long lsn){
    	return Formatter.format(new Date((lsn>>32)*1000),"yyMMdd");
    }

    

}
